/*
 *  $Id: Aura.cs 1067 2010-05-21 11:00:43Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System.Collections.Generic;

namespace Ragima {
    /// <summary>
    /// Абстрактная аура: эффект, влияющий не на владельца, а на другие объекты
    /// Параметр: влияет ли аура на врага и его объекты
    /// </summary>
    public abstract class AuraAffect : Affect {
        public readonly bool OwnAffected;
        public readonly bool EnemyAffected;
        private Dictionary<AbstractObject, AuraSubAffect> _objects;
        private Affect _example;

        public AuraAffect(bool own, bool enemy) {
            OwnAffected = own;
            EnemyAffected = enemy;
            _objects = new Dictionary<AbstractObject, AuraSubAffect>();
            _example = CreateSubAffect();
        }

        public override ElementType Element {
            get { return _example.Element; }
        }

        public override int BaseWeight {
            get {
                int weight = _example.BaseWeight;
                if (OwnAffected && EnemyAffected)
                    weight /= 2; else if (EnemyAffected)
                    weight = -weight;
                return weight * AverageWeightPercent / 100;
            }
        }

        public abstract int AverageWeightPercent { get; }

        public override void SetAffect() {
            base.SetAffect();
            Player player = Owner.Owner;
            if (OwnAffected)
                SetAffect(player);
            if (EnemyAffected)
                SetAffect(player.Enemy);
            UpdateAura(false);
        }
        public override void ResetAffect() {
            base.ResetAffect();
            Player player = Owner.Owner;
            if (OwnAffected)
                ResetAffect(player);
            if (EnemyAffected)
                ResetAffect(player.Enemy);
            ResetAura();
        }

        private void SetAffect(Player player) {
            player.GlobalSummary.OnCreatureSummon += DoCreatureSummon;
            player.GlobalSummary.OnLandSummon += DoLandSummon;
            player.GlobalSummary.OnDeath += DoDeath;
        }
        private void ResetAffect(Player player) {
            player.GlobalSummary.OnCreatureSummon -= DoCreatureSummon;
            player.GlobalSummary.OnLandSummon -= DoLandSummon;
            player.GlobalSummary.OnDeath -= DoDeath;
        }

        private void DoCreatureSummon(Creature creature) {
            OnAddObject(creature);
        }
        private void DoLandSummon(Land land) {
            OnAddObject(land);
        }
        private void DoDeath(AbstractObject obj) {
            OnRemoveObject(obj);
        }

        public void UpdateAura(bool force) {
            Player player = Owner.Owner;
            if (OwnAffected)
                UpdateAuraOnPlayer(force, player);
            if (EnemyAffected)
                UpdateAuraOnPlayer(force, player.Enemy);
        }
        private void UpdateAuraOnPlayer(bool force, Player player) {
            UpdateAura(force, player);
            foreach (SummonedObject obj in player.GetSummonedObjects())
                UpdateAura(force, obj);
        }

        public void UpdateAura(bool force, AbstractObject obj) {
            if (force) {
                ResetAuraIfSet(obj);
                SetAuraIfAccept(obj);
                return;
            }
            if (_objects.ContainsKey(obj))
                ResetAuraIfNotAccept(obj);
            else
                SetAuraIfAccept(obj);
        }

        public void SetAuraIfAccept(AbstractObject obj) {
            if (_objects.ContainsKey(obj))
                return;
            if (AcceptAura(obj))
                SetAura(obj);
        }
        public void ResetAuraIfNotAccept(AbstractObject obj) {
            if (!_objects.ContainsKey(obj))
                return;
            if (!AcceptAura(obj))
                ResetAura(obj);
        }

        private void ResetAuraIfSet(AbstractObject obj) {
            if (_objects.ContainsKey(obj))
                ResetAura(obj);
        }
        private void ResetAura() {
            AbstractObject[] objects = new AbstractObject[_objects.Count];
            _objects.Keys.CopyTo(objects, 0);
            foreach (AbstractObject obj in objects)
                ResetAura(obj);
        }

        /// <summary>
        /// При появлении нового объекта
        /// </summary>
        protected virtual void OnAddObject(AbstractObject obj) {
            SetAuraIfAccept(obj);
        }
        /// <summary>
        /// При исчезновении объекта
        /// </summary>
        protected virtual void OnRemoveObject(AbstractObject obj) {
            ResetAuraIfSet(obj);
        }

        protected bool AcceptOwner(AbstractObject obj) {
            return obj.Owner == Owner.Owner ? OwnAffected : EnemyAffected;
        }

        /// <summary>
        /// Проверка, должна ли аура действовать на данный объект
        /// </summary>
        /// <param name="obj">Проверяемый объект</param>
        /// <returns>true, если аура должна действовать</returns>
        protected virtual bool AcceptAura(AbstractObject obj) {
            return false;
        }
        /// <summary>
        /// Установка действия ауры на объект
        /// </summary>
        /// <param name="obj">Объект</param>
        protected virtual void SetAura(AbstractObject obj) {
            AuraSubAffect affect = new AuraSubAffect(CreateSubAffect());
            _objects.Add(obj, affect);
            obj.AddAffect(affect);
        }
        /// <summary>
        /// Снятие действия ауры с объекта
        /// </summary>
        /// <param name="obj">Объект</param>
        protected virtual void ResetAura(AbstractObject obj) {
            if (_objects.ContainsKey(obj)) {
                AuraSubAffect affect = _objects[obj];
                obj.RemoveAffect(affect);
                _objects.Remove(obj);
            }
        }
        /// <summary>
        /// Создать под-эффект ауры
        /// </summary>
        /// <returns>Созданный эффект</returns>
        protected abstract Affect CreateSubAffect();
    }

    /// <summary>
    /// Эффект-обертка над эффектами, накладываемыми аурами.
    /// </summary>
    public class AuraSubAffect : Affect {
        public readonly Affect SubAffect;

        public AuraSubAffect(Affect subAffect) {
            SubAffect = subAffect;
        }

        public override string Id {
            get { return SubAffect.Id; }
        }

        public override ElementType Element {
            get { return SubAffect.Element; }
        }

        public override bool Dispellable {
            get { return false; }
        }

        public override int BaseWeight {
            get { return SubAffect.BaseWeight; }
        }

        public override AbstractObject Owner {
            set {
                base.Owner = value;
                SubAffect.Owner = value;
            }
        }

        public override void SetAffect() {
            base.SetAffect();
            SubAffect.SetAffect();
        }

        public override void ResetAffect() {
            base.ResetAffect();
            SubAffect.ResetAffect();
        }

        public override void OnRemove() {
            base.OnRemove();
            SubAffect.OnRemove();
        }
    }

    /// <summary>
    /// Аура, деействующая на существ.
    /// </summary>
    public abstract class CreatureAuraAffect : AuraAffect {
        public CreatureAuraAffect(bool own, bool enemy) : base(own, enemy) {
        }

        public override int AverageWeightPercent {
            get { return OwnAffected ? (EnemyAffected ? 700 : 400) : (EnemyAffected ? 400 : 0); }
        }

        protected override bool AcceptAura(AbstractObject obj) {
            return AcceptOwner(obj) && obj is Creature && AcceptCreature((Creature) obj);
        }
        protected virtual bool AcceptCreature(Creature cr) {
            return true;
        }
    }
}
